package com.gr.experimental.model.impl.distr;

import com.gr.experimental.model.impl.distr.operations.CalculationException;
import com.gr.experimental.model.impl.distr.operations.Expression;

public class ArrayAccess implements Assignable, Expression {

	Descriptor descriptor;
	Expression idxExpression;

	public ArrayAccess(Descriptor descriptor, Expression idxExpression) {
		this.descriptor = descriptor;
		if (idxExpression.getValueType() != Integer.class) {
			throw new IllegalArgumentException("Index expression value is not Integer.");
		}
		if (!descriptor.getType().isArray()) {
			throw new IllegalArgumentException("Variable not an array.");
		}
		this.idxExpression = idxExpression;
	}

	@Override
	public void assign(Context context, Object value) throws AssignmentException {
		if (descriptor.isModifiable() == false) {
			throw new AssignmentException("Object " 
					+ descriptor.getSymbolName() + " not modifiable");
		}
		if (descriptor.getType().isArray() == false) {
			throw new AssignmentException("Object " 
					+ descriptor.getSymbolName() + " not an array");
		}
		Integer idx;
		try {
			idx = (Integer) idxExpression.getValue(context);
		} catch (CalculationException e) {
			throw new AssignmentException("Error calculating array index", e);
		}
		Object variableValue = context.getVariableValue(descriptor.getSymbolName());
		Object[] array = (Object[]) variableValue;
		if (!array.getClass().getComponentType().isAssignableFrom(value.getClass())) {
			throw new AssertionError();
		}
		array[idx] = value;
	}

	@Override
	public Object getValue(Context context) throws CalculationException {
		Integer idx = (Integer) idxExpression.getValue(context);
		Object variableValue = context.getVariableValue(descriptor.getSymbolName());
		assert variableValue.getClass().isArray();
		Object[] array = (Object[]) variableValue;
		return array[idx];
	}

	@Override
	public Class<?> getValueType() {
		return descriptor.getType().getComponentType();
	}
	
}
